package robocode.rescue.features;

import java.rmi.RemoteException;
import robocode.rescue.RobotInfo;
import robocode.rescue.interfaces.RMIRobotInterface;

/**
 *
 * @author Estevan
 */
public class Movimentacao {

    /**
     * Atributos!!!
     */
    final double PI = Math.PI;		//somente um constante.
    
    private RobotInfo[] robosInimigos;  //referência para os robôs inimigos.
    
    /**
     * Construtora!!!
     */
    public Movimentacao() {
        
    }


    /*
     * Métodos auxiliares criados por: Estevan Jantsk.
     * Estes métodos serão utilizados para auxiliar na movimentação dos robôs.
     */
    
    /**
     * Move o robô na coordenada x e y.
     */
    public void goTo(double x, double y, RobotInfo robo, RMIRobotInterface roboInt) throws RemoteException {
        double dist = 20;
        double angle = Math.toDegrees(absbearing(robo.getX(), robo.getY(), x, y));
        double r = turnTo(angle, robo, roboInt);
        roboInt.setAhead(dist * r);
        roboInt.execute();
    }
    
    /**
     * Move o robô no ângulo determinado pela RNA!
     */
    public void goToAngulo(double angulo, RobotInfo robo, RMIRobotInterface roboInt) throws RemoteException{
        
        double dist = 20;
        double angle = angulo;
        double r = turnToAngulo(angle, robo, roboInt);
        
        roboInt.setAhead(dist * r);
        roboInt.execute();
        
    }
    
    /**
     * Gira o menor ângulo para o robô ficar "heading", então retorna a direção
     * que o robô precisa movimentar-se.
     */
    public int turnTo(double angle, RobotInfo robo, RMIRobotInterface roboInt) throws RemoteException {
        double ang;
        int dir;
        ang = normaliseBearing(robo.getHeading() - angle);
        if (ang > 90) {
            ang -= 180;
            dir = -1;
        } else if (ang < -90) {
            ang += 180;
            dir = -1;
        } else {
            dir = 1;
        }
        roboInt.setTurnLeft(ang);
        return dir;
    }
    
    /**
     * Gira o menor ângulo para o robô ficar "heading", então retorna a direção
     * que o robô precisa movimentar-se.
     */
    public int turnToAngulo(double angle, RobotInfo robo, RMIRobotInterface roboInt) throws RemoteException {
        double ang;
        int dir;
        ang = normaliseBearing(robo.getHeading() - angle);
        if (ang > 90) {
            ang -= 180;
            dir = -1;
        } else if (ang < -90) {
            ang += 180;
            dir = -1;
        } else {
            dir = 1;
        }
        roboInt.setTurnLeft(ang);
        return dir;
    }
        
    /**
     * Retorna a distância entre dois pontos.
     */
    public double getRange(double x1, double y1, double x2, double y2) {
        double x = x2 - x1;
        double y = y2 - y1;
        double range = Math.sqrt(x * x + y * y);
        return range;
    }

    /**
     * Retorna a relação absoluta entre as coordenadas x e y
     *
     */
    public double absbearing(double x1, double y1, double x2, double y2) {

        double xo = x2 - x1;
        double yo = y2 - y1;
        double h = getRange(x1, y1, x2, y2);
        if (xo > 0 && yo > 0) {
            return Math.asin(xo / h);
        }
        if (xo > 0 && yo < 0) {
            return Math.PI - Math.asin(xo / h);
        }
        if (xo < 0 && yo < 0) {
            return Math.PI + Math.asin(-xo / h);
        }
        if (xo < 0 && yo > 0) {
            return 2.0 * Math.PI - Math.asin(-xo / h);
        }
        return 0;
    }

    /**
     * Se uma relação não está dentro do intervalo de-pi a pi, altera para
     * fornecer o menor ângulo
     *
     */
    public double normaliseBearing(double ang) {
        if (ang > PI) {
            ang -= 2 * PI;
        }
        if (ang < -PI) {
            ang += 2 * PI;
        }
        return ang;
    }
    
    /**
     * Este método irá retornar um array com as distâncias do nosso robô para todos o 4 robôs inimigos.
     * Ex: Entrada: parâmetro -> robo[1] (nosso robo de numero 1)
     *     Saída:   distInimigos[0] -> robo inimido de número 0 (robo prisioneiro, neste caso ele será ignorado).
     *              distInimigos[1] -> robo inimido de número 1
     *              distInimigos[2] -> robo inimido de número 2
     *              distInimigos[3] -> robo inimido de número 3
     *              distInimigos[4] -> robo inimido de número 4
     */
    public double[] distanciaRobo(RobotInfo robo, RobotInfo[] robosInimigos){
        double[] distInimigos = new double[5];
        
        for (int i=0; i<distInimigos.length; i++)
        {
            distInimigos[i] = getRange(robo.getX(), robo.getY(), robosInimigos[i].getX(), robosInimigos[i].getY());          
        }  
        return distInimigos;
    }
    
    /**
     * Este método irá retornar um array que possui todos os ângulos do robô com relação aos outros
     * robôs.
     * SERÁ UTILIZADO COMO ENTRADA PARA A REDE NEURAL!
     * 
     * Ex: Entrada: parâmetro -> robo[1] (nosso robo de numero 1)
     *     Saída:   angInimigos[0] -> robo inimido de número 0 (robo prisioneiro, neste caso ele será ignorado).
     *              angInimigos[1] -> robo inimido de número 1
     *              angInimigos[2] -> robo inimido de número 2
     *              angInimigos[3] -> robo inimido de número 3
     *              angInimigos[4] -> robo inimido de número 4
     */
    public double[] angulosRobo(RobotInfo robo, RobotInfo[] robosInimigos){
        double[] angInimigos = new double[5];
        
        //System.out.println("Robô: "+robo.getRobotIndex());
        
        for (int i=0; i<angInimigos.length; i++)
        {
            angInimigos[i] = Math.toDegrees(absbearing(robo.getX(), robo.getY(), robosInimigos[i].getX(), robosInimigos[i].getY()));
            //System.out.println("ângulo para o robô: "+i+" -> "+angInimigos[i]);
        }
        
        //System.out.println("");
        return angInimigos;
    }
    
    /**
     * ângulo que o gui pediu!
     * se for -> > 0 e < 90 = 45º
     * se for -> >= 90 e < 180 = 135º
     * se for -> >= 180 e < 270 = 225º
     * se for -> >= 270 e < 360 = 315º
     */
    public double anguloRNA(double valor){
        if(valor > 0 && valor < 90){
            return 60;
        }else if(valor >= 90 && valor < 180){
            return 120;
        }else if(valor >= 180 && valor < 270){
            return 250;
        }else{
            return 300;
        }
    }
}
